/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __PICTUREFORMAT_PICTUREFORMAT_H__
#define __PICTUREFORMAT_PICTUREFORMAT_H__

#include <ColorPacked.h>
#include <string.h>

namespace liba
{
namespace pictureformat
{


enum Format
{
	A8R8G8B8, R8G8B8A8,//32
	R8G8B8, R8G8B8CK, B8G8R8,// 24
	R5G6B5, A1R5G5B5, A4R4G4B4, R5G6B5CK, // 16
#if defined(IPHONE_VERSION) || defined(SAMSUNG_VERSION)
	PVR4A, PVR4, PVR2A, PVR2,	//compressed iphone
#endif
	FORMAT_SIZE
};

const unsigned char colorkey_red = 0;
const unsigned char colorkey_green = 0;
const unsigned char colorkey_blue = 0;
const unsigned char colorkey_red_near = 1;
const unsigned char colorkey_green_near = 0;
const unsigned char colorkey_blue_near = 0;
const unsigned short colorkey_short = 0;
const unsigned short colorkey_short_near = 1;

inline size_t FormatPixelSize(Format f)
{
	static size_t format_sizes[] =
	{
		4, 4,
		3, 3, 3,
		2, 2, 2, 2,
#if defined(IPHONE_VERSION) || defined(SAMSUNG_VERSION)
		1, 1, 1, 1	//TODO: actually compressed formats are less than 1 byte/pp
#endif
	};
	return format_sizes[f];
}

inline size_t GetAlphaBits(Format f)
{
	static int alpha_bits[] =
	{
		8, 8,
		0, 1, 0,
		0, 1, 4, 1,
#ifdef IPHONE_VERSION
		1, 0, 1, 0	//TODO: actually compressed formats are less than 1 byte/pp
#endif
		
	};
	return alpha_bits[f];
}

inline bool IsAlphaTransparent(unsigned char a)
{
	return a == 0; // Can be (a < 128), (a != 255), etc.
}

template<Format F>
struct RGBConverter
{};

template<>
struct RGBConverter<A8R8G8B8>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
//	static void * average(void * dst, const void * src12, const void * src34);
};

template<>
struct RGBConverter<R8G8B8A8>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
//	static void * average(void * dst, const void * src12, const void * src34);
};

template<>
struct RGBConverter<R8G8B8>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

template<>
struct RGBConverter<B8G8R8>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};


template<>
struct RGBConverter<R8G8B8CK>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

template<>
struct RGBConverter<R5G6B5>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

template<>
struct RGBConverter<A1R5G5B5>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

template<>
struct RGBConverter<A4R4G4B4>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

template<>
struct RGBConverter<R5G6B5CK>
{
	static const void * from(ColorPacked * color, const void * src);
	static void * to(const ColorPacked * color, void * dst);
};

} // namespace pictureformat
} // namepsace liba

#include "PictureFormatImpl.h"

namespace liba
{
namespace pictureformat
{

template<bool eq>
struct FormatConverterTraits
{
	template<Format Src, Format Dst>
	struct Inner
	{
		static void convert_scanline(const void * src, void * dst, int width) // slowest
		{
			ColorPacked tmp;
			for( int i = 0; i < width; ++i )
			{
				src = RGBConverter<Src>::from( &tmp, src );
				dst = RGBConverter<Dst>::to( &tmp, dst );
			}
		}
	};
};

template<>
struct FormatConverterTraits<true>
{
	template<Format Src, Format Dst>
	struct Inner
	{
		static void convert_scanline(const void * src, void * dst, size_t width) // fastest
		{
			memmove(dst, src, width*FormatPixelSize(Src));
		}
	};
};

template<Format Src, Format Dst>
struct FormatConverter : public FormatConverterTraits<Src==Dst>::template Inner<Src,Dst>
{};

template<Format Src>
void convert_scanline(Format Dst, const void * src, void * dst, size_t width)
{
	if( Src == Dst )
	{
		if( Dst == A8R8G8B8 )
			FormatConverterTraits<true>::Inner<Src,A8R8G8B8>::convert_scanline(src, dst, width);
		else
		if( Dst == R8G8B8A8 )
			FormatConverterTraits<true>::Inner<Src,R8G8B8A8>::convert_scanline(src, dst, width);
		else
		if( Dst == R8G8B8 )
			FormatConverterTraits<true>::Inner<Src,R8G8B8>::convert_scanline(src, dst, width);
		else
		if( Dst == B8G8R8 )
			FormatConverterTraits<true>::Inner<Src,R8G8B8>::convert_scanline(src, dst, width);
		else
		if( Dst == R8G8B8CK )
			FormatConverterTraits<true>::Inner<Src,R8G8B8CK>::convert_scanline(src, dst, width);
		else
		if( Dst == R5G6B5 )
			FormatConverterTraits<true>::Inner<Src,R5G6B5>::convert_scanline(src, dst, width);
		else
		if( Dst == A1R5G5B5 )
			FormatConverterTraits<true>::Inner<Src,A1R5G5B5>::convert_scanline(src, dst, width);
		else
		if( Dst == A4R4G4B4 )
			FormatConverterTraits<true>::Inner<Src,A4R4G4B4>::convert_scanline(src, dst, width);
		else
		if( Dst == R5G6B5CK )
			FormatConverterTraits<true>::Inner<Src,R5G6B5CK>::convert_scanline(src, dst, width);
		return;
	}

	if( Dst == A8R8G8B8 )
		FormatConverterTraits<false>::Inner<Src,A8R8G8B8>::convert_scanline(src, dst, width);
	else
	if( Dst == R8G8B8A8 )
		FormatConverterTraits<false>::Inner<Src,R8G8B8A8>::convert_scanline(src, dst, width);
	else
	if( Dst == R8G8B8 )
		FormatConverterTraits<false>::Inner<Src,R8G8B8>::convert_scanline(src, dst, width);
	else
	if( Dst == B8G8R8 )
		FormatConverterTraits<false>::Inner<Src,R8G8B8>::convert_scanline(src, dst, width);
	else
	if( Dst == R8G8B8CK )
		FormatConverterTraits<false>::Inner<Src,R8G8B8CK>::convert_scanline(src, dst, width);
	else
	if( Dst == R5G6B5 )
		FormatConverterTraits<false>::Inner<Src,R5G6B5>::convert_scanline(src, dst, width);
	else
	if( Dst == A1R5G5B5 )
		FormatConverterTraits<false>::Inner<Src,A1R5G5B5>::convert_scanline(src, dst, width);
	else
	if( Dst == A4R4G4B4 )
		FormatConverterTraits<false>::Inner<Src,A4R4G4B4>::convert_scanline(src, dst, width);
	else
	if( Dst == R5G6B5CK )
		FormatConverterTraits<false>::Inner<Src,R5G6B5CK>::convert_scanline(src, dst, width);
}
/*
template<Format F>
void average(void * dst, int dst_stride, int dst_width, int dst_height, const void * src, int src_stride)
{
	char * dst_char = static_cast<char *>( dst );
	const char * src_char = static_cast<const char *>( src );
	for( int y = 0; y != dst_height; ++y, src_char += 2*src_stride, dst_char += dst_stride )
	{
		const char * src12 = src_char;
		const char * src34 = src_char + src_stride;
		void * dst_curr = dst_char;
		for( int x = 0; x != dst_width; ++x )
		{
			dst_curr = RGBConverter<F>::average( dst_curr, src12, src34 );
			src12 += 2*FormatPixelSize(F);
			src34 += 2*FormatPixelSize(F);
		}
	}
}
*/
} // namespace pictureformat
} // namespace liba
using namespace liba;

#endif //__PICTUREFORMAT_PICTUREFORMAT_H__
